package com.example.demo.util;

import org.springframework.util.StringUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * alibaba的
 * @author 陈浩
 * @date 2021/1/9 15:36
 */
public class DateUtil {
    private static final ThreadLocal<Map<Integer, Boolean>> DATE_THREAD_LOCAL = new ThreadLocal();
    private static final ThreadLocal<Map<String, SimpleDateFormat>> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal();
    private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]");
    private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]");
    private static final Pattern date_ptrn3a = Pattern.compile("[yYmMdDhHsS]");
    private static final Pattern date_ptrn3b = Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/年月日,. :\"\\\\]+0*[ampAMP/]*$");
    private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]");
    private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]");
    private static final Pattern date_ptrn6 = Pattern.compile("(年|月|日|时|分|秒)+");
    public static final String DATE_FORMAT_10 = "yyyy-MM-dd";
    public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss";
    public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss";
    public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMAT_19_FORWARD_SLASH = "yyyy/MM/dd HH:mm:ss";
    private static final String MINUS = "-";

    private DateUtil() {
    }

    public static Date parseDate(String dateString, String dateFormat) throws ParseException {
        if (StringUtils.isEmpty(dateFormat)) {
            dateFormat = switchDateFormat(dateString);
        }

        return getCacheDateFormat(dateFormat).parse(dateString);
    }

    public static Date parseDate(String dateString) throws ParseException {
        return parseDate(dateString, switchDateFormat(dateString));
    }

    private static String switchDateFormat(String dateString) {
        int length = dateString.length();
        switch(length) {
            case 10:
                return "yyyy-MM-dd";
            case 11:
            case 12:
            case 13:
            case 15:
            case 16:
            case 18:
            default:
                throw new IllegalArgumentException("can not find date format for：" + dateString);
            case 14:
                return "yyyyMMddHHmmss";
            case 17:
                return "yyyyMMdd HH:mm:ss";
            case 19:
                return dateString.contains("-") ? "yyyy-MM-dd HH:mm:ss" : "yyyy/MM/dd HH:mm:ss";
        }
    }

    public static String format(Date date) {
        return format(date, (String)null);
    }

    public static String format(Date date, String dateFormat) {
        if (date == null) {
            return "";
        } else {
            if (StringUtils.isEmpty(dateFormat)) {
                dateFormat = "yyyy-MM-dd HH:mm:ss";
            }

            return getCacheDateFormat(dateFormat).format(date);
        }
    }

    private static DateFormat getCacheDateFormat(String dateFormat) {
        Map<String, SimpleDateFormat> dateFormatMap = (Map)DATE_FORMAT_THREAD_LOCAL.get();
        SimpleDateFormat dateFormatCached;
        if (dateFormatMap == null) {
            dateFormatMap = new HashMap();
            DATE_FORMAT_THREAD_LOCAL.set(dateFormatMap);
        } else {
            dateFormatCached = (SimpleDateFormat)((Map)dateFormatMap).get(dateFormat);
            if (dateFormatCached != null) {
                return dateFormatCached;
            }
        }

        dateFormatCached = new SimpleDateFormat(dateFormat);
        ((Map)dateFormatMap).put(dateFormat, dateFormatCached);
        return dateFormatCached;
    }

    public static boolean isADateFormat(Integer formatIndex, String formatString) {
        if (formatIndex == null) {
            return false;
        } else {
            Map<Integer, Boolean> isDateCache = (Map)DATE_THREAD_LOCAL.get();
            if (isDateCache == null) {
                isDateCache = new HashMap();
                DATE_THREAD_LOCAL.set(isDateCache);
            } else {
                Boolean isDateCachedData = (Boolean)((Map)isDateCache).get(formatIndex);
                if (isDateCachedData != null) {
                    return isDateCachedData;
                }
            }

            boolean isDate = isADateFormatUncached(formatIndex, formatString);
            ((Map)isDateCache).put(formatIndex, isDate);
            return isDate;
        }
    }

    public static boolean isADateFormatUncached(Integer formatIndex, String formatString) {
        if (isInternalDateFormat(formatIndex)) {
            return true;
        } else if (StringUtils.isEmpty(formatString)) {
            return false;
        } else {
            String fs = formatString;
            int length = formatString.length();
            StringBuilder sb = new StringBuilder(length);

            int separatorIndex;
            for(separatorIndex = 0; separatorIndex < length; ++separatorIndex) {
                char c = fs.charAt(separatorIndex);
                if (separatorIndex < length - 1) {
                    char nc = fs.charAt(separatorIndex + 1);
                    if (c == '\\') {
                        switch(nc) {
                            case ' ':
                            case ',':
                            case '-':
                            case '.':
                            case '\\':
                                continue;
                        }
                    } else if (c == ';' && nc == '@') {
                        ++separatorIndex;
                        continue;
                    }
                }

                sb.append(c);
            }

            fs = sb.toString();
            if (date_ptrn4.matcher(fs).matches()) {
                return true;
            } else {
                fs = date_ptrn5.matcher(fs).replaceAll("");
                fs = date_ptrn1.matcher(fs).replaceAll("");
                fs = date_ptrn2.matcher(fs).replaceAll("");
                separatorIndex = fs.indexOf(59);
                if (0 < separatorIndex && separatorIndex < fs.length() - 1) {
                    fs = fs.substring(0, separatorIndex);
                }

                if (!date_ptrn3a.matcher(fs).find()) {
                    return false;
                } else {
                    boolean result = date_ptrn3b.matcher(fs).matches();
                    if (result) {
                        return true;
                    } else {
                        result = date_ptrn6.matcher(fs).find();
                        return result;
                    }
                }
            }
        }
    }

    public static boolean isInternalDateFormat(int format) {
        switch(format) {
            case 14:
            case 15:
            case 16:
            case 17:
            case 18:
            case 19:
            case 20:
            case 21:
            case 22:
            case 27:
            case 28:
            case 29:
            case 30:
            case 31:
            case 32:
            case 33:
            case 34:
            case 35:
            case 36:
            case 45:
            case 46:
            case 47:
            case 50:
            case 51:
            case 52:
            case 53:
            case 54:
            case 55:
            case 56:
            case 57:
            case 58:
                return true;
            case 23:
            case 24:
            case 25:
            case 26:
            case 37:
            case 38:
            case 39:
            case 40:
            case 41:
            case 42:
            case 43:
            case 44:
            case 48:
            case 49:
            default:
                return false;
        }
    }

    public static void removeThreadLocalCache() {
        DATE_THREAD_LOCAL.remove();
        DATE_FORMAT_THREAD_LOCAL.remove();
    }
}
